package ro.inf.bomberman;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferStrategy;
import java.util.ArrayList;
import java.util.Iterator;

import javax.swing.JFrame;
import javax.swing.JPanel;

/**
 * Die Hauptklasse des Spiels. Hier wird alles gesteuert. Das Erstellen, Beenden
 * und zeichnen des Spiels, sowie die Logik.
 *
 * @author Michi
 *
 */
public class Game extends Canvas implements Runnable {

    private static final long serialVersionUID = 1L;
    private BufferStrategy strategy;
    private boolean gameRunning = true;
    private Player playerOne;
    private Player playerTwo;
    private GameField spielfeld;
    private JFrame container;
    private String playerOneName;
    private String playerTwoName;

    /**
     * Erstellen der Oberfläche, befüllen mit Farbe und festlegen der Größe.
     * @param playerOneName Spielername des ersten Spielers.
     * @param playerTwoName Spielername des zweiten Spielers.
     * @author Michi
     */
    public Game(String playerOneName, String playerTwoName) {
        this.playerOneName = playerOneName;
        this.playerTwoName = playerTwoName;
        JFrame container = new JFrame("Bomberman");
        this.container = container;
        JPanel panel = (JPanel) container.getContentPane();
        panel.setPreferredSize(new Dimension(840, 600));
        panel.setLayout(null);

        setBounds(0, 0, 840, 600);
        panel.add(this);

        // Übernehme das Zeichnen selbst
        setIgnoreRepaint(true);

        container.pack();
        container.setResizable(false);
        container.setVisible(true);

        // Wenn auf Schließen geklickt wird, wird das Spiel beendet
        container.addWindowListener(new WindowAdapter() {

            public void windowClosing(WindowEvent e) {
                //System.exit(0);
            }
        });

        // Fenster Focus anfordern
        requestFocus();

        createBufferStrategy(2);
        strategy = getBufferStrategy();
    }

    /**
     * Startet den Spielfluss.
     */
    public void run() {
        startGame(this);
    }

    /**
     * Hier wird die Spiel-Logik behandelt. Das Spiel läuft mit ca 100 Frames
     * pro Sekunde. Es wird unter anderem gezeichnet, Bewegungen durchgeführt,
     * Explosionen behandelt.
     *
     * @author Michi
     */
    public void gameLoop() {

        long lastLoopTime = System.currentTimeMillis();

        while (gameRunning) {

            // Speichern der Zeit die vergangen ist seit dem letzten
            // Schleifendurchlauf
            long delta = System.currentTimeMillis() - lastLoopTime;
            lastLoopTime = System.currentTimeMillis();

            Graphics2D g = (Graphics2D) strategy.getDrawGraphics();
            g.setColor(Color.black);
            g.fillRect(0, 0, 840, 600);

            // Alle Bombe explodieren lassen deren Zeit abgelaufen ist, den Rest
            // zeichnen, beziehungsweise eine Explosion zeichnen.
            ArrayList<Bomb> bombsToExplode = this.spielfeld.getBombsToExplode();
            Iterator<Bomb> it = bombsToExplode.iterator();
            while (it.hasNext()) {
                Bomb bomb = (Bomb) it.next();
                if (System.currentTimeMillis() > bomb.getTimeExplodes()) {
                    bomb.explode();
                    if (bomb.getSpielerNr() == 1) {
                        this.playerOne.setBombCounter(this.playerOne.getBombCounter() - 1);
                    } else {
                        this.playerTwo.setBombCounter(this.playerTwo.getBombCounter() - 1);
                    }
                } else {
                    if (System.currentTimeMillis() % 5 == 0) {
                        Sprite sprite = DrawCache.get().getSprite(
                                "ro/inf/bomberman/resources/Bombe.gif");
                        sprite.draw(g, bomb.getPosition().getX(), bomb.getPosition().getY());
                    } else if (System.currentTimeMillis() + 300 > bomb.getTimeExplodes()) {
                        Sprite sprite = DrawCache.get().getSprite(
                                "ro/inf/bomberman/resources/explosion.gif");
                        sprite.draw(g, bomb.getPosition().getX(), bomb.getPosition().getY());
                    } else {
                        Sprite sprite = DrawCache.get().getSprite(
                                "ro/inf/bomberman/resources/bombefunken.gif");
                        sprite.draw(g, bomb.getPosition().getX(), bomb.getPosition().getY());
                    }
                }
            }

            // Blöcke und Spieler auf das Feld zeichnen
            for (int i = 0; i < 840; i += 40) {
                for (int k = 0; k < 600; k += 40) {
                    Field[][] fieldArray = this.spielfeld.getSpielfeld();
                    Field field = fieldArray[(int) i / 40][(int) k / 40];
                    if (field.containsObject() == true
                            && !(field.getRegisteredObject() instanceof Player)) {
                        Block block = (Block) field.getRegisteredObject();
                        if (block.getExplodable() == false) {
                            Sprite sprite = DrawCache.get().getSprite(
                                    "ro/inf/bomberman/resources/festerBlock.gif");
                            sprite.draw(g, block.getPosition().getX(), block.getPosition().getY());
                        } else {
                            Sprite sprite = DrawCache.get().getSprite(
                                    "ro/inf/bomberman/resources/explodableBlock.gif");
                            sprite.draw(g, block.getPosition().getX(), block.getPosition().getY());
                        }
                    } else if (field.containsObject() == true) {
                        Player player = (Player) field.getRegisteredObject();
                        Sprite sprite;
                        if (player.getSpielerNummer() == 1) {
                            if (System.currentTimeMillis() % 5 == 0) {
                                sprite = DrawCache.get().getSprite(
                                        "ro/inf/bomberman/resources/blueman.gif");
                            } else if (System.currentTimeMillis() % 3 == 0) {
                                sprite = DrawCache.get().getSprite(
                                        "ro/inf/bomberman/resources/bluemanwalkingright.gif");
                            } else {
                                sprite = DrawCache.get().getSprite(
                                        "ro/inf/bomberman/resources/bluemanwalkingleft.gif");
                            }
                        } else {
                            if (System.currentTimeMillis() % 5 == 0) {
                                sprite = DrawCache.get().getSprite(
                                        "ro/inf/bomberman/resources/redman.gif");
                            } else if (System.currentTimeMillis() % 3 == 0) {
                                sprite = DrawCache.get().getSprite(
                                        "ro/inf/bomberman/resources/redmanwalkingright.gif");
                            } else {
                                sprite = DrawCache.get().getSprite(
                                        "ro/inf/bomberman/resources/redmanwalkingleft.gif");
                            }
                        }
                        sprite.draw(g, player.getPosition().getX(), player.getPosition().getY());
                    }
                }
            }

            // Spieler eins bewegen
            playerOne.move(delta);

            // Spieler zwei bewegen
            playerTwo.move(delta);

            // Das Gezeichnete anzeigen
            g.dispose();
            strategy.show();

            // Lasse den Thread so lange schlafen das konstante 100 FPS
            // entstehen
            if ((System.currentTimeMillis() - lastLoopTime) == 0) {
                try {
                    Thread.sleep(10);
                } catch (Exception e) {
                }
                // System.out.println("FPS " + 1000/(System.currentTimeMillis()
                // - lastLoopTime));
            } else {
                long sleepTime = 10 - (System.currentTimeMillis() - lastLoopTime);
                try {
                    Thread.sleep(sleepTime);
                } catch (Exception e) {
                }
                // System.out.println("FPS " + 1000/(System.currentTimeMillis()
                // - lastLoopTime));
            }
        }
    }

    /**
     * Hier wird das Start-Spielfeld initialisiert. Die Player werden angelegt,
     * Blöcke platziert, Spieler platziert.
     *
     * @author Michi
     * @param g Game Das Spiel für das das Spielfeld aufgebaut werden soll
     */
    public void startGame(Game g) {
        // Spielfeld instanzieren und FelderArray erstellen
        this.gameRunning = true;
        this.spielfeld = new GameField();

        // Player initialisieren
        this.playerOne = new Player(this.playerOneName, new Size(40, 40),
                new Coordinate(40, 40), this.spielfeld, g);
        this.playerTwo = new Player(this.playerTwoName, new Size(40, 40),
                new Coordinate(680, 440), this.spielfeld, g);

        // Blöcke initialisieren
        for (int i = 0; i < 840; i += 40) {
            for (int k = 0; k < 600; k += 40) {
                if (i == 0) {
                    Coordinate coord = new Coordinate(i, k);
                    Block block = new Block(coord, false, this.spielfeld, 0);
                    this.spielfeld.mapCoordToField(coord).registerObject(block);
                } else if (i == 800) {
                    Coordinate coord = new Coordinate(i, k);
                    Block block = new Block(coord, false, this.spielfeld, 0);
                    this.spielfeld.mapCoordToField(coord).registerObject(block);
                } else if (k == 560) {
                    Coordinate coord = new Coordinate(i, k);
                    Block block = new Block(coord, false, this.spielfeld, 0);
                    this.spielfeld.mapCoordToField(coord).registerObject(block);
                } else if (k == 0) {
                    Coordinate coord = new Coordinate(i, k);
                    Block block = new Block(coord, false, this.spielfeld, 0);
                    this.spielfeld.mapCoordToField(coord).registerObject(block);
                } else if (i % 80 == 0 && k % 80 == 0) {
                    Coordinate coord = new Coordinate(i, k);
                    Block block = new Block(coord, false, this.spielfeld, 0);
                    this.spielfeld.mapCoordToField(coord).registerObject(block);
                } else if ((i % 30 + k % 30) == 0 && this.spielfeld.mapCoordToField(new Coordinate(i, k)).containsObject() == false) {
                    Coordinate coord = new Coordinate(i, k);
                    Block block = new Block(coord, true, this.spielfeld, 0);
                    this.spielfeld.mapCoordToField(coord).registerObject(block);
                } else if ((i % 5 + k % 3) == 0 && this.spielfeld.mapCoordToField(new Coordinate(i, k)).containsObject() == false) {
                    Coordinate coord = new Coordinate(i, k);
                    Block block = new Block(coord, true, this.spielfeld, 0);
                    this.spielfeld.mapCoordToField(coord).registerObject(block);
                } else if ((i % 3 + k % 5) == 0 && this.spielfeld.mapCoordToField(new Coordinate(i, k)).containsObject() == false) {
                    Coordinate coord = new Coordinate(i, k);
                    Block block = new Block(coord, true, this.spielfeld, 0);
                    this.spielfeld.mapCoordToField(coord).registerObject(block);
                }
            }
        }
        // Schleife starten
        g.gameLoop();
    }

    /**
     * Gibt zurück ob das Spiel läuft oder nicht.
     * @return boolean Läuft das Spiel oder nicht.
     */
    public boolean getGameRunning() {
        return this.gameRunning;
    }

    /**
     * Beendet das Spiel und updatet Highscore.
     *
     * @param g Das Spiel das beendet werden soll.
     * @author Michi
     */
    public void stopGame(Game g) {
        this.gameRunning = false;

        PlayerModel playerOneModel = new PlayerModel(this.playerOneName);
        PlayerModel playerTwoModel = new PlayerModel(this.playerTwoName);
        if (playerOne.getPlayerState() == true) {
        //    System.out.println(playerOneModel.updateHighscore(100));
        //   System.out.println(playerTwoModel.updateHighscore(50));
        	playerOneModel.updateHighscore(100);
        	playerTwoModel.updateHighscore(50);
        } else {
        //    System.out.println(playerOneModel.updateHighscore(50));
        //   System.out.println(playerTwoModel.updateHighscore(100));
        	playerOneModel.updateHighscore(50);
        	playerTwoModel.updateHighscore(100);
        }

        this.playerOne.setNaechsterSpieler(1);
        Thread thread = Thread.currentThread();
        this.container.setVisible(false);
        thread.stop();
    }
}
